package tools

import (
	"time"
)

//I2s covert int64 to time.Duration as Second
func I2s(d int64) time.Duration {
	return time.Duration(d) * time.Second
}

//I2ms covert int64 to time.Duration as Millisecond
func I2ms(d int64) time.Duration {
	return time.Duration(d) * time.Millisecond
}
func MultiI2ms(dList []int64) []time.Duration {
	retList := make([]time.Duration, len(dList))
	for i, d := range dList {
		retList[i] = time.Duration(d) * time.Millisecond
	}
	return retList
}

//S2I covert time.Duration to int64 as Second
func S2I(d time.Duration) int64 {
	return int64(d / time.Second)
}

//MS2I covert time.Duration to int64 as Millisecond
func MS2I(d time.Duration) int64 {
	return int64(d / time.Millisecond)
}

//Timer is a countdown timer
type Timer struct {
	start time.Time
	end   time.Time
	stamp time.Duration
}

//NewTimer return a new Timer
func NewTimer() *Timer {
	t := new(Timer)
	return t
}

//Reset timer to restart countdown
func (t *Timer) Reset() {
	t.CountDown(t.stamp)
}

//Done return if countdown done
func (t *Timer) Done() bool {
	return t.Rest() <= 0
}

//CountDown try to countdown from now, and duration as d
func (t *Timer) CountDown(d time.Duration) {
	t.start = time.Now()
	t.stamp = d
	t.end = t.start.Add(d)
}

//CountDownS is shorthand of CountDown(I2s(d))
func (t *Timer) CountDownS(d int64) {
	t.CountDown(I2s(d))
}

//CountDownMS is shorthand of CountDown(I2ms(d))
func (t *Timer) CountDownMS(d int64) {
	t.CountDown(I2ms(d))
}

//Rest return rest duration to countdown done
//result may be a negative, if result < 0, means done
func (t *Timer) Rest() time.Duration {
	return t.end.Sub(time.Now())
}

//RestS is shorthand of S2I(Rest(d))
func (t *Timer) RestS() int64 {
	return S2I(t.end.Sub(time.Now()))
}

//RestMS is shorthand of MS2I(Rest(d))
func (t *Timer) RestMS() int64 {
	return MS2I(t.end.Sub(time.Now()))
}

//IsSameDay 判定两个秒级时间戳是否代表同一天
func IsSameDay(l, r int64) bool {
	lt := time.Unix(l, 0)
	rt := time.Unix(r, 0)
	ly, lm, ld := lt.Date()
	ry, rm, rd := rt.Date()
	return ly == ry && lm == rm && ld == rd
}

//DayDiff 计算两个秒级时间戳天数差距， l - r
func DayDiff(l, r int64) int {
	const DAY = time.Hour * 24
	lt := time.Unix(l, 0)
	rt := time.Unix(r, 0)
	lt = DayStart(lt)
	rt = DayStart(rt)
	diff := lt.Sub(rt)
	return int(diff / DAY)
}

//DayStart 计算时间t的当天起始时间
func DayStart(t time.Time) time.Time {
	y, m, d := t.Date()
	t = time.Date(y, m, d, 0, 0, 0, 0, t.Location())
	return t
}

//调试用时间

type nowFunc = func() time.Time

type timeDecorator = func(nowFunc) nowFunc

//DebugTimer 调试用计时器
type DebugTimer struct {
	base       nowFunc
	decorators []timeDecorator
	now        nowFunc
}

//NewDebugTimer 创建新的调试计时器，默认使用time.Now初始化
func NewDebugTimer(base nowFunc) *DebugTimer {
	if base == nil {
		base = time.Now
	}
	t := new(DebugTimer)
	t.decorators = make([]timeDecorator, 0)
	t.SetBase(base)
	return t
}

func (t *DebugTimer) adjust() {
	t.now = t.base
	for _, d := range t.decorators {
		t.now = d(t.now)
	}
}

//SetBase 调整基础计时器
func (t *DebugTimer) SetBase(base nowFunc) {
	t.base = base
	t.adjust()
}

//Now 返回当前时间
func (t *DebugTimer) Now() time.Time {
	if t == nil || t.now == nil {
		return time.Now()
	}
	return t.now()
}

//AddDecorator 添加新的装饰器
func (t *DebugTimer) AddDecorator(ds ...timeDecorator) {
	if t == nil || len(ds) <= 0 {
		return
	}
	for _, d := range ds {
		t.decorators = append(t.decorators, d)
		t.now = d(t.now)
	}
}

//ClearDecorator 清除所有装饰器
func (t *DebugTimer) ClearDecorator() {
	t.decorators = make([]timeDecorator, 0)
	t.adjust()
}

//CreateTDCover 创建覆盖装饰器，直接覆盖原计时方法
var CreateTDCover = func(f nowFunc) timeDecorator {
	return func(nowFunc) nowFunc {
		return f
	}
}

//CreateTDConst 创建常量装饰器，使用返回固定时间now的方法覆盖原计时方法
var CreateTDConst = func(now time.Time) timeDecorator {
	return func(nowFunc) nowFunc {
		return func() time.Time { return now }
	}
}

//CreateTDAdd 创建偏移装饰器，在原计时基础上加上偏移值d
var CreateTDAdd = func(d time.Duration) timeDecorator {
	return func(now nowFunc) nowFunc {
		return func() time.Time { return now().Add(d) }
	}
}

//CreateTDStart 创建起点装饰器，设置当前时间为start，效果等同于偏移装饰器
var CreateTDStart = func(start time.Time) timeDecorator {
	return func(now nowFunc) nowFunc {
		d := now().Sub(start)
		return func() time.Time { return now().Add(d) }
	}
}

//CreateTDRate 创建速率装饰器，计时速率变为rate倍
var CreateTDRate = func(rate float64) timeDecorator {
	return func(now nowFunc) nowFunc {
		start := now()
		return func() time.Time {
			tmp := now()
			d := time.Duration(float64(tmp.Sub(start)) * rate)
			return start.Add(d)
		}
	}
}

//提供包方法供外部使用
var debugTimer = NewDebugTimer(time.Now)

//Now 返回调试使用当前时间
func Now() time.Time {
	return debugTimer.Now()
}

//SetNow 将Now设置为f
func SetNow(f nowFunc) {
	debugTimer.ClearDecorator()
	debugTimer.SetBase(f)
}

//SetNowConst 使Now返回固定时间now
func SetNowConst(now time.Time) {
	SetNow(func() time.Time { return now })
}

//SetNowAdd 使Now返回时间加偏移值d
func SetNowAdd(d time.Duration) {
	debugTimer.AddDecorator(CreateTDAdd(d))
}

//SetNowStart 设置now为当前时间，之后Now返回的时间将从now后计时
func SetNowStart(now time.Time) {
	SetNowAdd(time.Now().Sub(now))
}

//SetNowRate 调节时间速率为rate
func SetNowRate(rate float64) {
	debugTimer.AddDecorator(CreateTDRate(rate))
}
